//
// Implementation of N4562 std::experimental::any (merged into C++17) for C++11 compilers.
//
// See also:
//   + http://en.cppreference.com/w/cpp/any
//   + http://en.cppreference.com/w/cpp/experimental/any
//   + http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4562.html#any
//   + https://cplusplus.github.io/LWG/lwg-active.html#2509
//
//
// Copyright (c) 2016 Denilson das Mercês Amorim
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef LINB_ANY_HPP
#define LINB_ANY_HPP
#if (defined(_HAS_CXX17) && _HAS_CXX17 == 1) || (defined(__cplusplus) && __cplusplus >= 201703L)
#include <any>
#else//__cplusplus <= 201402L
#include <typeinfo>
#include <type_traits>
#include <stdexcept>
#include <utility>
#include <tp/c++.h>
#if defined(PARTICLE)
#if !defined(__cpp_exceptions) && !defined(ANY_IMPL_NO_EXCEPTIONS) && !defined(ANY_IMPL_EXCEPTIONS)
#   define ANY_IMPL_NO_EXCEPTIONS
# endif
#else
// you can opt-out of exceptions by definining ANY_IMPL_NO_EXCEPTIONS,
// but you must ensure not to cast badly when passing an `any' object to any_cast<T>(any)
#endif
#if defined(PARTICLE)
#if !defined(__cpp_rtti) && !defined(ANY_IMPL_NO_RTTI) && !defined(ANY_IMPL_RTTI)
#   define ANY_IMPL_NO_RTTI
# endif
#else
// you can opt-out of RTTI by defining ANY_IMPL_NO_RTTI,
// in order to disable functions working with the typeid of a type
#endif

namespace std {
  class bad_any_cast: public std::bad_cast {
  public:
    const char* what() const noexcept override {
      return "bad any cast";
    }
  };
  class any final {
  public:
    /// Constructs an object of type any with an empty state.
    any():
      vtable(nullptr) {}
    /// Constructs an object of type any with an equivalent state as other.
    any(const any& rhs):
      vtable(rhs.vtable) {
      if (!rhs.empty()) {
        rhs.vtable->copy(rhs.storage, this->storage);
      }
    }
    /// Constructs an object of type any with a state equivalent to the original state of other.
    /// rhs is left in a valid but otherwise unspecified state.
    any(any&& rhs) noexcept:
      vtable(rhs.vtable) {
      if (!rhs.empty()) {
        rhs.vtable->move(rhs.storage, this->storage);
        rhs.vtable = nullptr;
      }
    }
    /// Same effect as this->clear().
    ~any() {
      this->clear();
    }
    /// Constructs an object of type any that contains an object of type T direct-initialized with std::forward<ValueType>(value).
    ///
    /// T shall satisfy the CopyConstructible requirements, otherwise the program is ill-formed.
    /// This is because an `any` may be copy constructed into another `any` at any time, so a copy should always be allowed.
    template<typename ValueType, typename = typename std::enable_if<!std::is_same<typename std::decay<ValueType>::type, any>::value>::type>
    any(ValueType&& value) {
      static_assert(std::is_copy_constructible<typename std::decay<ValueType>::type>::value,
        "T shall satisfy the CopyConstructible requirements.");
      this->construct(std::forward<ValueType>(value));
    }
    /// Has the same effect as any(rhs).swap(*this). No effects if an exception is thrown.
    any& operator=(const any& rhs) {
      any(rhs).swap(*this);
      return *this;
    }
    /// Has the same effect as any(std::move(rhs)).swap(*this).
    ///
    /// The state of *this is equivalent to the original state of rhs and rhs is left in a valid
    /// but otherwise unspecified state.
    any& operator=(any&& rhs) noexcept {
      any(std::move(rhs)).swap(*this);
      return *this;
    }
    /// Has the same effect as any(std::forward<ValueType>(value)).swap(*this). No effect if a exception is thrown.
    ///
    /// T shall satisfy the CopyConstructible requirements, otherwise the program is ill-formed.
    /// This is because an `any` may be copy constructed into another `any` at any time, so a copy should always be allowed.
    template<typename ValueType, typename = typename std::enable_if<!std::is_same<typename std::decay<ValueType>::type, any>::value>::type>
    any& operator=(ValueType&& value) {
      static_assert(std::is_copy_constructible<typename std::decay<ValueType>::type>::value,
        "T shall satisfy the CopyConstructible requirements.");
      any(std::forward<ValueType>(value)).swap(*this);
      return *this;
    }
    /// If not empty, destroys the contained object.
    void clear() noexcept {
      if (!empty()) {
        this->vtable->destroy(storage);
        this->vtable = nullptr;
      }
    }
    /// Returns true if *this has no contained object, otherwise false.
    bool empty() const noexcept {
      return this->vtable == nullptr;
    }
#ifndef ANY_IMPL_NO_RTTI
    /// If *this has a contained object of type T, typeid(T); otherwise typeid(void).
    const std::type_info& type() const noexcept {
      return empty() ? typeid(void) : this->vtable->type();
    }
#endif
    /// Exchange the states of *this and rhs.
    void swap(any& rhs) noexcept {
      if (this->vtable != rhs.vtable) {
        any tmp(std::move(rhs));
        // move from *this to rhs.
        rhs.vtable = this->vtable;
        if (this->vtable != nullptr) {
          this->vtable->move(this->storage, rhs.storage);
          //this->vtable = nullptr; -- unneeded, see below
        }
        // move from tmp (previously rhs) to *this.
        this->vtable = tmp.vtable;
        if (tmp.vtable != nullptr) {
          tmp.vtable->move(tmp.storage, this->storage);
          tmp.vtable = nullptr;
        }
      } else // same types
      {
        if (this->vtable != nullptr)
          this->vtable->swap(this->storage, rhs.storage);
      }
    }
  private: // Storage and Virtual Method Table
    union storage_union {
      using stack_storage_t = typename std::aligned_storage<2 * sizeof(void*), std::alignment_of<void*>::value>::type;
      void* dynamic;
      stack_storage_t     stack;      // 2 words for e.g. shared_ptr
    };
    /// Base VTable specification.
    struct vtable_type {
      // Note: The caller is responssible for doing .vtable = nullptr after destructful operations
      // such as destroy() and/or move().
#ifndef ANY_IMPL_NO_RTTI
    /// The type of the object this vtable is for.
      const std::type_info& (*type)() noexcept;
#endif
      /// Destroys the object in the union.
      /// The state of the union after this call is unspecified, caller must ensure not to use src anymore.
      void(*destroy)(storage_union&) noexcept;
      /// Copies the **inner** content of the src union into the yet unitialized dest union.
      /// As such, both inner objects will have the same state, but on separate memory locations.
      void(*copy)(const storage_union& src, storage_union& dest);
      /// Moves the storage from src to the yet unitialized dest union.
      /// The state of src after this call is unspecified, caller must ensure not to use src anymore.
      void(*move)(storage_union& src, storage_union& dest) noexcept;
      /// Exchanges the storage between lhs and rhs.
      void(*swap)(storage_union& lhs, storage_union& rhs) noexcept;
    };
    /// VTable for dynamically allocated storage.
    template<typename T>
    struct vtable_dynamic {
#ifndef ANY_IMPL_NO_RTTI
      static const std::type_info& type() noexcept {
        return typeid(T);
      }
#endif
      static void destroy(storage_union& storage) noexcept {
        //assert(reinterpret_cast<T*>(storage.dynamic));
        delete reinterpret_cast<T*>(storage.dynamic);
      }
      static void copy(const storage_union& src, storage_union& dest) {
        dest.dynamic = new T(*reinterpret_cast<const T*>(src.dynamic));
      }
      static void move(storage_union& src, storage_union& dest) noexcept {
        dest.dynamic = src.dynamic;
        src.dynamic = nullptr;
      }
      static void swap(storage_union& lhs, storage_union& rhs) noexcept {
        // just exchage the storage pointers.
        std::swap(lhs.dynamic, rhs.dynamic);
      }
    };
    /// VTable for stack allocated storage.
    template<typename T>
    struct vtable_stack {
#ifndef ANY_IMPL_NO_RTTI
      static const std::type_info& type() noexcept {
        return typeid(T);
      }
#endif
      static void destroy(storage_union& storage) noexcept {
        reinterpret_cast<T*>(&storage.stack)->~T();
      }
      static void copy(const storage_union& src, storage_union& dest) {
        new (&dest.stack) T(reinterpret_cast<const T&>(src.stack));
      }
      static void move(storage_union& src, storage_union& dest) noexcept {
        // one of the conditions for using vtable_stack is a nothrow move constructor,
        // so this move constructor will never throw a exception.
        new (&dest.stack) T(std::move(reinterpret_cast<T&>(src.stack)));
        destroy(src);
      }
      static void swap(storage_union& lhs, storage_union& rhs) noexcept {
        storage_union tmp_storage;
        move(rhs, tmp_storage);
        move(lhs, rhs);
        move(tmp_storage, lhs);
      }
    };
    /// Whether the type T must be dynamically allocated or can be stored on the stack.
    template<typename T>
    struct requires_allocation:
      std::integral_constant<bool,
      !(std::is_nothrow_move_constructible<T>::value      // N4562 §6.3/3 [any.class]
        && sizeof(T) <= sizeof(storage_union::stack)
        && std::alignment_of<T>::value <= std::alignment_of<storage_union::stack_storage_t>::value)> {};
    /// Returns the pointer to the vtable of the type T.
    template<typename T>
    static vtable_type* vtable_for_type() {
      using VTableType = typename std::conditional<requires_allocation<T>::value, vtable_dynamic<T>, vtable_stack<T>>::type;
      static vtable_type table = {
  #ifndef ANY_IMPL_NO_RTTI
        VTableType::type,
  #endif
        VTableType::destroy,
        VTableType::copy, VTableType::move,
        VTableType::swap,
      };
      return &table;
    }
  protected:
    template<typename T>
    friend const T* any_cast(const any* operand) noexcept;
    template<typename T>
    friend T* any_cast(any* operand) noexcept;
#ifndef ANY_IMPL_NO_RTTI
    /// Same effect as is_same(this->type(), t);
    bool is_typed(const std::type_info& t) const {
      return is_same(this->type(), t);
    }
#endif
#ifndef ANY_IMPL_NO_RTTI
    /// Checks if two type infos are the same.
    ///
    /// If ANY_IMPL_FAST_TYPE_INFO_COMPARE is defined, checks only the address of the
    /// type infos, otherwise does an actual comparision. Checking addresses is
    /// only a valid approach when there's no interaction with outside sources
    /// (other shared libraries and such).
    static bool is_same(const std::type_info& a, const std::type_info& b) {
#ifdef ANY_IMPL_FAST_TYPE_INFO_COMPARE
      return &a == &b;
#else
      return a == b;
#endif
    }
#endif
    /// Casts (with no type_info checks) the storage pointer as const T*.
    template<typename T>
    const T* cast() const noexcept {
      return requires_allocation<typename std::decay<T>::type>::value ?
        reinterpret_cast<const T*>(storage.dynamic) :
        reinterpret_cast<const T*>(&storage.stack);
    }
    /// Casts (with no type_info checks) the storage pointer as T*.
    template<typename T>
    T* cast() noexcept {
      return requires_allocation<typename std::decay<T>::type>::value ?
        reinterpret_cast<T*>(storage.dynamic) :
        reinterpret_cast<T*>(&storage.stack);
    }
  private:
    storage_union storage; // on offset(0) so no padding for align
    vtable_type* vtable;
    template<typename ValueType, typename T>
    typename std::enable_if<requires_allocation<T>::value>::type
      do_construct(ValueType&& value) {
      storage.dynamic = new T(std::forward<ValueType>(value));
    }
    template<typename ValueType, typename T>
    typename std::enable_if<!requires_allocation<T>::value>::type
      do_construct(ValueType&& value) {
      new (&storage.stack) T(std::forward<ValueType>(value));
    }
    /// Chooses between stack and dynamic allocation for the type decay_t<ValueType>,
    /// assigns the correct vtable, and constructs the object on our storage.
    template<typename ValueType>
    void construct(ValueType&& value) {
      using T = typename std::decay<ValueType>::type;
      this->vtable = vtable_for_type<T>();
      do_construct<ValueType, T>(std::forward<ValueType>(value));
    }
  };
  namespace detail {
    template<typename ValueType>
    inline ValueType any_cast_move_if_true(typename std::remove_reference<ValueType>::type* p, std::true_type) {
      return std::move(*p);
    }
    template<typename ValueType>
    inline ValueType any_cast_move_if_true(typename std::remove_reference<ValueType>::type* p, std::false_type) {
      return *p;
    }
  }
#if defined(__cplusplus) && __cplusplus >= 201402L
  template <class _ValueType, class... _Types>
  [[nodiscard]] any make_any(_Types&&... _Args) { // construct an any containing a _ValueType initialized with _Args...
    return any{ in_place_type<_ValueType>, std::forward<_Types>(_Args)... };
  }
  template <class _ValueType, class _Elem, class... _Types>
  [[nodiscard]] any make_any(initializer_list<_Elem> _Ilist, _Types&&... _Args) {
    // construct an any containing a _ValueType initialized with _Ilist and _Args...
    return any{ in_place_type<_ValueType>, _Ilist, std::forward<_Types>(_Args)... };
  }
#else
  template <class _ValueType, class... _Types>
  [[nodiscard]] any make_any(_Types&&... _Args) { // construct an any containing a _ValueType initialized with _Args...
    return any{ in_place_type_t<_ValueType>{}, std::forward<_Types>(_Args)... };
  }
  template <class _ValueType, class _Elem, class... _Types>
  [[nodiscard]] any make_any(initializer_list<_Elem> _Ilist, _Types&&... _Args) {
    // construct an any containing a _ValueType initialized with _Ilist and _Args...
    return any{ in_place_type_t<_ValueType>{}, _Ilist, std::forward<_Types>(_Args)... };
  }
#endif
  /// Performs *any_cast<add_const_t<remove_reference_t<ValueType>>>(&operand), or throws bad_any_cast on failure.
  template<typename ValueType>
  inline ValueType any_cast(const any& operand) {
    auto p = any_cast<typename std::add_const<typename std::remove_reference<ValueType>::type>::type>(&operand);
#ifndef ANY_IMPL_NO_EXCEPTIONS
    if (p == nullptr) throw bad_any_cast();
#endif
    return *p;
  }
  /// Performs *any_cast<remove_reference_t<ValueType>>(&operand), or throws bad_any_cast on failure.
  template<typename ValueType>
  inline ValueType any_cast(any& operand) {
    auto p = any_cast<typename std::remove_reference<ValueType>::type>(&operand);
#ifndef ANY_IMPL_NO_EXCEPTIONS
    if (p == nullptr) throw bad_any_cast();
#endif
    return *p;
  }
  ///
  /// If ValueType is MoveConstructible and isn't a lvalue reference, performs
  /// std::move(*any_cast<remove_reference_t<ValueType>>(&operand)), otherwise
  /// *any_cast<remove_reference_t<ValueType>>(&operand). Throws bad_any_cast on failure.
  ///
  template<typename ValueType>
  inline ValueType any_cast(any&& operand) {
    using can_move = std::integral_constant<bool,
      std::is_move_constructible<ValueType>::value
      && !std::is_lvalue_reference<ValueType>::value>;
    auto p = any_cast<typename std::remove_reference<ValueType>::type>(&operand);
#ifndef ANY_IMPL_NO_EXCEPTIONS
    if (p == nullptr) throw bad_any_cast();
#endif
    return detail::any_cast_move_if_true<ValueType>(p, can_move());
  }
  /// If operand != nullptr && operand->type() == typeid(ValueType), a pointer to the object
  /// contained by operand, otherwise nullptr.
  template<typename ValueType>
  inline const ValueType* any_cast(const any* operand) noexcept {
    using T = typename std::decay<ValueType>::type;
#ifndef ANY_IMPL_NO_RTTI
    if (operand && operand->is_typed(typeid(T)))
#else
    if (operand && operand->vtable == any::vtable_for_type<T>())
#endif
      return operand->cast<ValueType>();
    else
      return nullptr;
  }
  /// If operand != nullptr && operand->type() == typeid(ValueType), a pointer to the object
  /// contained by operand, otherwise nullptr.
  template<typename ValueType>
  inline ValueType* any_cast(any* operand) noexcept {
    using T = typename std::decay<ValueType>::type;
#ifndef ANY_IMPL_NO_RTTI
    if (operand && operand->is_typed(typeid(T)))
#else
    if (operand && operand->vtable == any::vtable_for_type<T>())
#endif
      return operand->cast<ValueType>();
    else
      return nullptr;
  }
  inline void swap(std::any& lhs, std::any& rhs) noexcept {
    lhs.swap(rhs);
  }
}
#endif
#endif
